package pe.edu.unmsm.juegoia.model;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Pablo & Marlon
 */
public abstract class JuegoTyAModel {

    public final static int TURNO_JUGADOR_1 = 1;
    public final static int TURNO_JUGADOR_2 = 2;
    public final static int JUGADOR_1 = 1;
    public final static int JUGADOR_2 = 2;
    private List<TableroObserver> tableroObservers = new ArrayList<>();
    private List<MovimientoObserver> movimientoObservers = new ArrayList<>();
    protected Integer[][] tablero = new Integer[7][7];
    protected Posicion fichaSeleccionada = null;
    protected List<Posicion> movimientosDisponibles = null;
    protected List<Posicion> movimientosValidosTodos = null;
    protected boolean iniciado;
    protected int cantJugadas;
    protected int numFichasRestantesJ1 = 5;
    protected int numFichasRestantesJ2 = 9;
    protected int turnoActual;
    protected int jugadorGanador = 0;
    protected Posicion fichaElegidaUltimo;
    protected Posicion movElegidoUltimo;
    protected Posicion fichaElegidaUltimoJ2;
    protected Posicion movElegidoUltimoJ2;
    protected String movimientoJ1;
    protected String movimientoJ2;

    public abstract void mover(int x, int y);

    public void iniciarNuevoJuego() {
        // iniciar tablero, iniciar turno, iniciar reloj, inicializar fichas 
        // restantes de cada jugador
        turnoActual = TURNO_JUGADOR_1;
        jugadorGanador = 0;
        numFichasRestantesJ1 = 5;
        numFichasRestantesJ2 = 9;

        cantJugadas = 0;
        int i, j;
        for (i = 0; i < 3; i++) {
            for (j = 0; j < i + 2; j++) {
                tablero[i][j] = 0;
            }
            tablero[i][j] = 2;
            tablero[i][j + 1] = 2;
        }
        tablero[3][0] = 1;
        tablero[3][1] = 0;
        tablero[3][2] = 0;
        tablero[3][3] = 0;
        tablero[3][4] = 0;
        tablero[3][5] = 2;
        tablero[3][6] = 2;
        tablero[4][1] = 1;
        tablero[4][2] = 1;
        tablero[4][3] = 0;
        tablero[4][4] = 0;
        tablero[4][5] = 0;
        tablero[4][6] = 2;
        tablero[5][2] = 1;
        tablero[5][3] = 0;
        tablero[5][4] = 0;
        tablero[5][5] = 0;
        tablero[5][6] = 0;
        tablero[6][3] = 1;
        tablero[6][4] = 0;
        tablero[6][5] = 0;
        tablero[6][6] = 0;

        notifytableroObservers();
    }

    public void setFichaSeleccionada(int x, int y) {
        fichaSeleccionada = new Posicion(x, y);

        calcularMovimientos();
    }

    public List<Posicion> getMovimientosDisponibles() {
        return movimientosDisponibles;
    }

    public Integer[][] getTablero() {
        return tablero;
    }

    public boolean existeFichaSileccionada() {
        if (fichaSeleccionada != null) {
            return true;
        }
        return false;
    }

    public int getFicha(int x, int y) {
        return tablero[x][y];
    }

    public int getJugadorTurnoActual() {
        return turnoActual;
    }

    public int getJugadorGanador() {
        return jugadorGanador;
    }

    // Metodos no accesibles desde clases exteriores
    protected boolean posicionesFinales(int jugador) {
        int x = fichaSeleccionada.getPosX();
        int y = fichaSeleccionada.getPosY();
        if (jugador == JUGADOR_1) {
            if ((x == 0 && y == 3) || (x == 1 && y == 4)
                    || (x == 2 && y == 5) || (x == 3 && y == 6)) {
                return true;
            }
            return false;
        } else { // jugador 2
            if ((x == 3 && y == 0) || (x == 4 && y == 1)
                    || (x == 5 && y == 2) || (x == 6 && y == 3)) {
                return true;
            }
            return false;
        }
    }

    protected void cambiarTurno() {
        if (turnoActual == TURNO_JUGADOR_1) {
            turnoActual = TURNO_JUGADOR_2;
        } else {
            turnoActual = TURNO_JUGADOR_1;
        }
    }

    protected int contieneFicha(int x, int y) {
        try {
            if (tablero[x][y] == JUGADOR_1) {
                return JuegoModel.JUGADOR_1;
            } else if (tablero[x][y] == JuegoModel.JUGADOR_2) {
                return JuegoModel.JUGADOR_2;
            } else {
                return 0;
            }
        } catch (NullPointerException e) {
            return -1;
        }
    }

    protected boolean isCasilleroVacio(int x, int y) {
        if (tablero[x][y] == JUGADOR_1 || tablero[x][y] == JUGADOR_2) {
            return false;
        }

        return true;
    }

    protected boolean existeGanador() {
        if (blancasGanan()) {
            jugadorGanador = JUGADOR_1;
            return true;
        }

        if (rojasGanan()) {
            jugadorGanador = JUGADOR_2;
            return true;
        }
        return false;
    }

    public boolean blancasGanan() {//Verificar si las fichas blancas ganan
        if ((tablero[0][3] == JUGADOR_1) || (tablero[1][4] == JUGADOR_1)
                || (tablero[2][5] == JUGADOR_1) || (tablero[3][6] == JUGADOR_1)) {
            return true;
        } else {
            return (false);
        }
    }

    public boolean rojasGanan() {//Verificar si las fichas rojas ganan
        if (numFichasRestantesJ1 == 0) {
            return true;
        } else {
            return false;
        }
    }

    public void registerObserver(TableroObserver o) {
        tableroObservers.add(o);
    }

    public void removeObserver(TableroObserver o) {
        tableroObservers.remove(o);
    }

    public void notifytableroObservers() {
        for (int i = 0; i < tableroObservers.size(); i++) {
            TableroObserver observer = tableroObservers.get(i);
            observer.updateTablero();
        }
    }

    public void registerObserver(MovimientoObserver o) {
        movimientoObservers.add(o);
    }

    public void removeObserver(MovimientoObserver o) {
        movimientoObservers.remove(o);
    }

    public void notifyMovimientoObservers() {
        for (int i = 0; i < movimientoObservers.size(); i++) {
            MovimientoObserver observer = movimientoObservers.get(i);
            observer.updateMovimientos();
        }
    }

    protected boolean isMovimientoComida(int x, int y) {
        int jugadorContrincante = (turnoActual == JUGADOR_1) ? JUGADOR_2 : JUGADOR_1;
        if (tablero[x][y] == jugadorContrincante) {
            return true;
        }
        return false;
    }

    protected boolean isMovimientoLibreValido(int x, int y) {
        for (Posicion movimiento : movimientosValidosTodos) {
            if ((x == movimiento.getPosX()) && (y == movimiento.getPosY())) {
                return true;
            }
        }

        return false;
    }

    private void calcularMovimientos() {
        int x = fichaSeleccionada.getPosX();
        int y = fichaSeleccionada.getPosY();
        movimientosValidosTodos = new ArrayList<>();
        movimientosDisponibles = new ArrayList<>();
        if (turnoActual == TURNO_JUGADOR_1) {

            if (x == 0) { // si estoy en el borde superior izquierdo
                movimientosValidosTodos.add(new Posicion(x, y + 1));
                if (isCasilleroVacio(x, y + 1)) {
                    movimientosDisponibles.add(new Posicion(x, y + 1));
                }
            } else if (y == 6) // si estoy en el borde superior derecho
            {
                movimientosValidosTodos.add(new Posicion(x - 1, y));
                if (isCasilleroVacio(x - 1, y)) {
                    movimientosDisponibles.add(new Posicion(x - 1, y));
                }
            } else if (!posicionesFinales(JUGADOR_1)) {
                movimientosValidosTodos.add(new Posicion(x - 1, y));
                movimientosValidosTodos.add(new Posicion(x, y + 1));
                if (isCasilleroVacio(x - 1, y)) {
                    movimientosDisponibles.add(new Posicion(x - 1, y));
                }
                if (isCasilleroVacio(x, y + 1)) {
                    movimientosDisponibles.add(new Posicion(x, y + 1));
                }
            }
        } else { // jugador 2

            if (y == 0) { // si estoy en el borde inferior izquierdo
                if (!isFichaSeleccionadaFila(6)) {
                    if (tablero[x + 1][y] != JUGADOR_2) {
                        movimientosValidosTodos.add(new Posicion(x + 1, y));
                        if (isCasilleroVacio(x + 1, y)) {
                            movimientosDisponibles.add(new Posicion(x + 1, y));
                        }
                    }
                }
            } else if (x == 6) { // si estoy en el borde inferior derecho
                if (!isFichaSeleccionadaFila(6)) {
                    if (tablero[x][y - 1] != JUGADOR_2) {
                        movimientosValidosTodos.add(new Posicion(x, y - 1));
                        if (isCasilleroVacio(x, y - 1)) {
                            movimientosDisponibles.add(new Posicion(x, y - 1));
                        }
                    }
                }
            } else if (!posicionesFinales(JUGADOR_2)) {
                if (tablero[x][y - 1] != JUGADOR_2) {
                    movimientosValidosTodos.add(new Posicion(x, y - 1));
                    if (isCasilleroVacio(x, y - 1)) {
                        movimientosDisponibles.add(new Posicion(x, y - 1));
                    }
                }

                if (tablero[x + 1][y] != JUGADOR_2) {
                    movimientosValidosTodos.add(new Posicion(x + 1, y));
                    if (isCasilleroVacio(x + 1, y)) {
                        movimientosDisponibles.add(new Posicion(x + 1, y));
                    }
                }
            }
        }

        notifyMovimientoObservers();
    }

    protected boolean isFichaSeleccionadaFila(int fila) {
        boolean isFilaI = false;
        Posicion ficha = fichaSeleccionada;
        int posX = fichaSeleccionada.getPosX(), posY = fichaSeleccionada.getPosY();
        switch (fila) {
            case 0:
                if ((posX == 0 && posY == 3) || (posX == 1 && posY == 4)
                        || (posX == 2 && posY == 5) || (posX == 3 && posY == 6)) {
                    isFilaI = true;
                }
                break;
            case 6:
                if ((posX == 3 && posY == 0) || (posX == 4 && posY == 1)
                        || (posX == 5 && posY == 2) || (posX == 6 && posY == 3)) {
                    isFilaI = true;
                }
                break;
        }
        return isFilaI;
    }

    protected boolean isPosicionPerteneceFila(Posicion p, int fila) {
        boolean isFilaI = false;
        int posX = p.getPosX(), posY = p.getPosY();
        switch (fila) {
            case 0:
                if ((posX == 0 && posY == 3) || (posX == 1 && posY == 4)
                        || (posX == 2 && posY == 5) || (posX == 3 && posY == 6)) {
                    isFilaI = true;
                }
                break;
            case 1:
                if ((posX == 0 && posY == 2) || (posX == 1 && posY == 3)
                        || (posX == 2 && posY == 4) || (posX == 3 && posY == 5)
                        || (posX == 4 && posY == 6)) {
                    isFilaI = true;
                }
                break;
            case 2:
                if ((posX == 0 && posY == 1) || (posX == 1 && posY == 2)
                        || (posX == 2 && posY == 3) || (posX == 3 && posY == 4)
                        || (posX == 4 && posY == 5) || (posX == 5 && posY == 6)) {
                    isFilaI = true;
                }
                break;
            case 3:
                if ((posX == 0 && posY == 0) || (posX == 1 && posY == 1)
                        || (posX == 2 && posY == 2) || (posX == 3 && posY == 3)
                        || (posX == 4 && posY == 4) || (posX == 5 && posY == 5)
                        || (posX == 6 && posY == 6)) {
                    isFilaI = true;
                }
                break;
            case 4:
                if ((posX == 1 && posY == 0) || (posX == 2 && posY == 1)
                        || (posX == 3 && posY == 2) || (posX == 4 && posY == 3)
                        || (posX == 5 && posY == 4) || (posX == 6 && posY == 5)) {
                    isFilaI = true;
                }
                break;
            case 5:
                if ((posX == 2 && posY == 0) || (posX == 3 && posY == 1)
                        || (posX == 4 && posY == 2) || (posX == 5 && posY == 3)
                        || (posX == 6 && posY == 4)) {
                    isFilaI = true;
                }
                break;
            case 6:
                if ((posX == 3 && posY == 0) || (posX == 4 && posY == 1)
                        || (posX == 5 && posY == 2) || (posX == 6 && posY == 3)) {
                    isFilaI = true;
                }
                break;
        }
        return isFilaI;
    }

    protected void calcularMovimiento(int jugador) {
        int posX = -1, posY = -1;
        int posXMov = -1, posYMov = -1;
        Posicion ficha = null, movimiento = null;
        String temp = "";
        switch (jugador) {
            case JUGADOR_1:
                posX = fichaElegidaUltimo.getPosX();
                posY = fichaElegidaUltimo.getPosY();
                posXMov = movElegidoUltimo.getPosX();
                posYMov = movElegidoUltimo.getPosY();
                ficha = fichaElegidaUltimo;
                movimiento = movElegidoUltimo;
                break;
            case JUGADOR_2:
                posX = fichaElegidaUltimoJ2.getPosX();
                posY = fichaElegidaUltimoJ2.getPosY();
                posXMov = movElegidoUltimoJ2.getPosX();
                posYMov = movElegidoUltimoJ2.getPosY();
                ficha = fichaElegidaUltimoJ2;
                movimiento = movElegidoUltimoJ2;
                break;
        }

        if (isPosicionPerteneceFila(ficha, 0)) {
            temp = "A";
        }
        if (isPosicionPerteneceFila(ficha, 1)) {
            temp = "B";
        }
        if (isPosicionPerteneceFila(ficha, 2)) {
            temp = "C";
        }
        if (isPosicionPerteneceFila(ficha, 3)) {
            temp = "D";
        }
        if (isPosicionPerteneceFila(ficha, 4)) {
            temp = "E";
        }
        if (isPosicionPerteneceFila(ficha, 5)) {
            temp = "F";
        }
        if (isPosicionPerteneceFila(ficha, 6)) {
            temp = "G";
        }
        
        if (posX == 0) {
            temp += "1";
        }
        if (posX == 1) {
            temp += "2";
        }
        if (posX == 2) {
            temp += "3";
        }
        if (posX == 3) {
            temp += "4";
        }
        if (posX == 4) {
            temp += "5";
        }
        if (posX == 5) {
            temp += "6";
        }
        if (posX == 6) {
            temp += "7";
        }
        
        temp += " >> ";
        
        if (isPosicionPerteneceFila(movimiento, 0)) {
            temp += "A";
        }
        if (isPosicionPerteneceFila(movimiento, 1)) {
            temp += "B";
        }
        if (isPosicionPerteneceFila(movimiento, 2)) {
            temp += "C";
        }
        if (isPosicionPerteneceFila(movimiento, 3)) {
            temp += "D";
        }
        if (isPosicionPerteneceFila(movimiento, 4)) {
            temp += "E";
        }
        if (isPosicionPerteneceFila(movimiento, 5)) {
            temp += "F";
        }
        if (isPosicionPerteneceFila(movimiento, 6)) {
            temp += "G";
        }
        
        if (posXMov == 0) {
            temp += "1";
        }
        if (posXMov == 1) {
            temp += "2";
        }
        if (posXMov == 2) {
            temp += "3";
        }
        if (posXMov == 3) {
            temp += "4";
        }
        if (posXMov == 4) {
            temp += "5";
        }
        if (posXMov == 5) {
            temp += "6";
        }
        if (posXMov == 6) {
            temp += "7";
        }

        switch (jugador) {
            case JUGADOR_1:
                movimientoJ1 = temp;
                break;
            case JUGADOR_2:
                movimientoJ2 = temp;
                break;
        }
    }

    public Posicion getFichaElegidaUltimo() {
        return fichaElegidaUltimo;
    }

    public Posicion getMovElegidoUltimo() {
        return movElegidoUltimo;
    }

    public Posicion getFichaElegidaUltimoJ2() {
        return fichaElegidaUltimoJ2;
    }

    public Posicion getMovElegidoUltimoJ2() {
        return movElegidoUltimoJ2;
    }

    public String getMovimientoJ1() {
        return movimientoJ1;
    }

    public String getMovimientoJ2() {
        return movimientoJ2;
    }

    public abstract void setNivelMaquina(int i);
}
